#!/usr/bin/python2.4
#
#Copyright 2009 Google Inc.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
#


"""This module contains the request handler to generate html page tree.

It generates all the landing and non-landing html pages including the sitemap
file and stores them as a compressed blob in the GAE db.
"""

import logging
import string
from django.utils import simplejson
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

import base_request_handler
import constants
import db_utils
import page_tree_utils
import tree_node
import id_encryption
import digital_content_wizard_exceptions

TRIX_COLUMNS = ['col' + x for x in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
ID_COLUMN = 'colA'
PDF_LOCATION = '/data/'
PAGE_PROPERTIES_COLUMN_START = 5  # Column F


class URLNamePair(object):
  """Maintains name/url pairs."""

  def __init__(self, name, url):
    """The constructor.

    Args:
      name: Text to display.
      url: A url string that the name maps to.
    """
    self.name = name
    self.url = url


class PageTreeCreator(base_request_handler.BaseRequestHandler):
  """The request handler to generate the html page tree.

  This class generates all the landing and non-landing pages including the
  sitemap file and stores them as a compressed blob in the GAE db.
  """

  def GetSiteURL(self, url):
    """Return the current site url.

    Args:
      url: The url string from which to extract the site url string.

    Returns:
      The site url string.
    """
    slash = url.rindex('/')
    return url[0:slash]

  def FilterMetadata(self, metadata, display_flags):
    """Returns filtered metadata to be displayed on the page table.

    Args:
      metadata: Array of strings representing metadata.
      display_flags: Array of numbers representing display flags for metadata
      blob on landing page and metadata table on the leve page.
    Returns:
      Array of metadata value strings that need to be displayed on the level
      page.
    """
    i = 0
    metadata_out = []
    for display_flag in display_flags:
      try:
        display_flag_number = constants.DISPLAY_FLAGS[display_flag]
        if display_flag_number & 1:
          if len(metadata) > i:
            metadata_out.append(metadata[i])
          else:
            metadata_out.append('')
      except ValueError:
        logging.warning('Metadata display flag is invalid.')
      i += 1
    return metadata_out

  def CreatePageTree(self):
    """Reads metadata from db and generate page tree and sitemap urls.

    It iterates over metadata rows and generates page tree. Simultaneously it
    extracts urls for sitemap.
    """
    page_tree = tree_node.PageTree()
    db_handler = db_utils.DbHandler()

    # Get all metadata and generate page tree from it.
    logging.debug('Reading metadata and generating in-memory page tree')
    metadata_set = db_handler.GetMetadata(self.current_version_id)
    display_flags = db_handler.GetDisplayFlags(self.current_version_id)
    for metadata in metadata_set:
      metadata_to_be_displayed = [metadata.title]
      metadata_to_be_displayed.extend(self.FilterMetadata(
          metadata.rest_metadata.split('\t')[len(constants.PAGE_PROPERTIES):],
          display_flags))
      page_tree.CreateNodes(self.no_of_levels, metadata.id,
                            '\t'.join(metadata_to_be_displayed),
                            metadata.sorting_column1, metadata.sorting_column2,
                            metadata.sorting_column3)
      node = page_tree.GetNode(metadata.id)
      while node and node.GetKey() != 'ROOT':
        url = node.GetKey() + '.html'
        if url not in self.urls:
          self.urls.append(url)
        node = page_tree.GetNode(node.GetParent())

  def GeneratePageTreeJson(self):
    """Generates json object and writes .js file to db.

    This function generates json ojbect from page tree. This json object is
    used to update page tree in db. Then this json object is written to
    json_metadata.js file and added to db.
    """
    logging.debug('Generating json object for page tree and navigation bar')
    page_tree = tree_node.PageTree()
    page_tree_json = {}
    navigation_json = {}
    template_values = {}
    json_blob = []
    db_handler = db_utils.DbHandler()
    # Generate json object from page tree.
    page_tree.CreateJsonStructure(navigation_element=navigation_json,
                                  page_tree_element=page_tree_json)
    json_blob = [navigation_json]
    encoded_json = simplejson.dumps(json_blob, ensure_ascii=False)
    template_values['treeData'] = encoded_json
    self.page_map['json_metadata.js'] = self.GenerateHtml('json_metadata.js',
                                                          template_values)
    # Write the extended json object into db so that it can be used during lazy
    # fetching of pages.
    json_blob = [page_tree_json]
    encoded_json = simplejson.dumps(json_blob, ensure_ascii=False)
    db_handler.UpdatePageTree(self.current_version_id, encoded_json)

  def ReadGlobalMetadata(self, template_values):
    """Fetch the fields for global metadata from the db.

    Args:
      template_values: Dictionary object used as an out parameter for
      populating global data values.
    """
    page_tree_utils_instance = page_tree_utils.GQLUtils(
        self.current_version_id)
    row_id = 1
    field_values = page_tree_utils_instance.GetGlobalRowByNumber(row_id)
    while ''.join(field_values):
      value_unicode = field_values[0].encode(constants.ENCODING)
      value_html = value_unicode.replace('\n', '<br>')
      template_values[field_values[-1]] = value_html
      row_id += 1
      field_values = page_tree_utils_instance.GetGlobalRowByNumber(row_id)
    template_values['img_path'] = constants.IMAGE_PATH

  def GenerateSubHtmls(self, template_values):
    """Generates HTML snippets for parts of HTML page.

    This method generates generic HTML snippets present on all the pages. It
    generates header, footer, sidebar snippets and populates templatE_values
    accordingly.

    Args:
      template_values: Dictionary object that stores the required string to
      detemplatize the django template.
    """
    template_values['header_html'] = self.GenerateHtml('header.html',
                                                       template_values)
    template_values['footer_html'] = self.GenerateHtml('footer.html',
                                                       template_values)
    template_values['sidebar_html'] = self.GenerateHtml('sidebar.html',
                                                        template_values)

  def GenerateGlobalFrames(self, template_values):
    """Generates the header, footer, sidebar and main pages.

    Also adds the html blobs into the db.

    Args:
      template_values: Dictionary object containing values needed to
      detemplatize main.html.
    """
    # Initialize
    template_values['version_id'] = str(self.current_version_id)
    template_values['noOfLevels'] = self.no_of_levels
    # generate main html
    self.GenerateSubHtmls(template_values)
    main_html = self.GenerateHtml('main.html', template_values)
    self.page_map['main.html'] = main_html

  def GenerateSiteMap(self):
    """Generates sitemap.xml file from all the urls.

    Also adds the xml blob into the db.
    """
    # Initialize
    logging.debug('Generating sitemap')
    template_values = {}
    template_values['version_id'] = str(self.current_version_id)
    site_url = self.GetSiteURL(self.request.uri)
    template_values['site_url'] = site_url
    template_values['all_urls'] = self.urls
    sitemap_xml = self.GenerateHtml('sitemap.xml', template_values)
    self.page_map['sitemap.xml'] = sitemap_xml
    sitemap_html = self.GenerateHtml('sitemap.html', template_values)
    self.page_map['sitemap.html'] = sitemap_html

  def ClearPageTree(self):
    """Clears page tree data."""
    page_tree = tree_node.PageTree()
    page_tree.Clear()

  def GenerateBackgroundCss(self, template_values):
    """Generates templatized css background.css and adds it to db.

    Args:
      template_values: Dictionary object that stores the required string to
      detemplatize the django template.
    """
    logging.debug('Generating background css')
    self.page_map['background.css'] = self.GenerateHtml('background.css',
                                                        template_values)

  def get(self):
    """GET method for this request handler."""
    db_handler = db_utils.DbHandler()
    template_values = {}
    status = db_handler.GetStatus()
    self.current_version_id = db_handler.GetLatestVersion()
    try:
      self.no_of_levels = db_handler.GetNumberOfLevels(self.current_version_id)
      self.page_map = {}
      self.urls = []
      if status == constants.GENERATE_WEBSITE:
        self.ReadGlobalMetadata(template_values)
        self.GenerateGlobalFrames(template_values)
        self.CreatePageTree()
        self.GeneratePageTreeJson()
        self.GenerateSiteMap()
        self.GenerateBackgroundCss(template_values)
        logging.debug('Adding basic pages to db')
        db_handler.AddPages(self.page_map, self.current_version_id)
        db_handler.UpdateStatus(constants.GET_METADATA)
        self.ClearPageTree()
      self.response.out.write(self.GenerateHtml('redirection.html',
                                                {'redirect_url': 'admin'}))
    except digital_content_wizard_exceptions.DbException, e:
      redirect_url = '/delete?version_id=' + id_encryption.Encrypt(
          self.current_version_id)
      logging.error(e)
      self.response.out.write(self.GenerateHtml('error.html',
                                                {'message': str(e),
                                                 'redirect_url':
                                                 redirect_url}))


application = webapp.WSGIApplication([('/pagetree', PageTreeCreator)],
                                     debug=True)


def main():
  run_wsgi_app(application)


if __name__ == '__main__':
  main()
